import { PrismaClient } from "@prisma/client";
import * as fs from "fs";
import * as path from "path";

const prisma = new PrismaClient();

/**
 * 数据库恢复工具
 * 从JSON备份文件恢复数据库数据
 */
class DatabaseRestore {
  private backupDir: string;

  constructor(backupDir: string) {
    this.backupDir = backupDir;
  }

  /**
   * 执行完整的数据库恢复
   */
  async restoreDatabase(): Promise<void> {
    try {
      console.log("🚀 开始数据库恢复...");

      // 检查备份目录
      if (!fs.existsSync(this.backupDir)) {
        throw new Error(`备份目录不存在: ${this.backupDir}`);
      }

      // 读取备份信息
      const backupInfo = await this.readBackupInfo();
      console.log(`📋 备份时间: ${backupInfo.backupTime}`);
      console.log(`📊 备份表数量: ${backupInfo.totalTables}`);

      // 获取所有JSON文件
      const jsonFiles = this.getJsonFiles();
      console.log(`📁 发现 ${jsonFiles.length} 个备份文件`);

      let successCount = 0;
      let failCount = 0;

      // 逐个文件进行恢复（按依赖顺序）
      for (let i = 0; i < jsonFiles.length; i++) {
        const file = jsonFiles[i];
        const tableName = path.basename(file, '.json');
        
        try {
          console.log(`🔄 [${i + 1}/${jsonFiles.length}] 正在恢复表: ${tableName}`);
          
          // 读取JSON数据
          const data = await this.readJsonFile(file);
          
          if (data && data.length > 0) {
            // 恢复数据到数据库
            await this.restoreTableData(tableName, data);
            console.log(`✅ 表 ${tableName} 恢复成功 (${data.length} 条记录)`);
            successCount++;
          } else {
            console.log(`ℹ️  表 ${tableName} 无数据，跳过恢复`);
            successCount++;
          }
        } catch (error) {
          console.error(`❌ 表 ${tableName} 恢复失败:`, error);
          failCount++;
          
          // 如果是外键约束错误，提供更详细的错误信息
          if (error.code === 'P2003') {
            console.error(`💡 提示: 表 ${tableName} 的外键约束失败，请检查依赖表是否已正确恢复`);
          }
        }
      }

      console.log(`📊 恢复任务完成 - 成功: ${successCount}, 失败: ${failCount}`);
      
      if (failCount > 0) {
        throw new Error(`恢复过程中有 ${failCount} 个表失败`);
      }

    } catch (error) {
      console.error('❌ 数据库恢复任务失败:', error);
      throw error;
    }
  }

  /**
   * 读取备份信息文件
   */
  private async readBackupInfo(): Promise<any> {
    const infoPath = path.join(this.backupDir, 'backup-info.json');
    
    if (!fs.existsSync(infoPath)) {
      console.log('⚠️  未找到备份信息文件，继续恢复...');
      return { backupTime: 'Unknown', totalTables: 0 };
    }

    const content = fs.readFileSync(infoPath, 'utf8');
    return JSON.parse(content);
  }

  /**
   * 获取所有JSON文件，按照依赖顺序排序
   */
  private getJsonFiles(): string[] {
    const files = fs.readdirSync(this.backupDir);
    const jsonFiles = files
      .filter(file => file.endsWith('.json') && file !== 'backup-info.json')
      .map(file => path.join(this.backupDir, file));

    // 定义表的依赖顺序（父表在前，子表在后）
    const tableOrder = [
      'User',                    // 用户表（无依赖）
      'ArticleCategory',         // 文章分类（无依赖）
      'CaseCategory',           // 案例分类（无依赖）
      'ProductCategory',        // 产品分类（无依赖）
      'Article',                // 文章（依赖 ArticleCategory）
      'Case',                   // 案例（依赖 CaseCategory）
      'Product',                // 产品（依赖 ProductCategory）
      'ArticleComment',         // 文章评论（依赖 Article）
      'CaseComment',            // 案例评论（依赖 Case）
      'ProductComment',         // 产品评论（依赖 Product）
      'FileUpload'              // 文件上传（无依赖）
    ];

    // 按照依赖顺序排序文件
    const orderedFiles: string[] = [];
    
    // 先添加有明确顺序的表
    for (const tableName of tableOrder) {
      const file = jsonFiles.find(f => path.basename(f, '.json') === tableName);
      if (file) {
        orderedFiles.push(file);
      }
    }
    
    // 再添加其他未定义顺序的表
    for (const file of jsonFiles) {
      const tableName = path.basename(file, '.json');
      if (!tableOrder.includes(tableName)) {
        orderedFiles.push(file);
      }
    }

    return orderedFiles;
  }

  /**
   * 读取JSON文件
   */
  private async readJsonFile(filePath: string): Promise<any[]> {
    try {
      const content = fs.readFileSync(filePath, 'utf8');
      return JSON.parse(content);
    } catch (error) {
      console.error(`读取文件失败 ${filePath}:`, error);
      throw error;
    }
  }

  /**
   * 恢复表数据
   */
  private async restoreTableData(tableName: string, data: any[]): Promise<void> {
    try {
      // 使用动态属性访问Prisma模型
      const model = (prisma as any)[tableName];
      if (!model) {
        throw new Error(`表 ${tableName} 不存在`);
      }

      // 清空现有数据（可选）
      // await model.deleteMany();

      // 批量插入数据
      if (data.length > 0) {
        // 对于大数据量，分批处理
        const batchSize = 1000;
        for (let i = 0; i < data.length; i += batchSize) {
          const batch = data.slice(i, i + batchSize);
          await model.createMany({
            data: batch,
            skipDuplicates: true, // 跳过重复数据
          });
        }
      }
      
    } catch (error) {
      console.error(`恢复表 ${tableName} 数据失败:`, error);
      throw error;
    }
  }

  /**
   * 验证恢复结果
   */
  async verifyRestore(): Promise<void> {
    try {
      console.log("🔍 验证恢复结果...");

      const jsonFiles = this.getJsonFiles();
      
      for (const file of jsonFiles) {
        const tableName = path.basename(file, '.json');
        const backupData = await this.readJsonFile(file);
        
        if (backupData.length > 0) {
          const model = (prisma as any)[tableName];
          if (model) {
            const currentCount = await model.count();
            console.log(`📊 表 ${tableName}: 备份 ${backupData.length} 条，当前 ${currentCount} 条`);
          }
        }
      }
      
    } catch (error) {
      console.error('验证恢复结果失败:', error);
    }
  }
}

/**
 * 主函数
 */
async function main() {
  // 从命令行参数获取备份目录
  const backupDir = process.argv[2];
  
  if (!backupDir) {
    console.error('❌ 请指定备份目录');
    console.log('使用方法: npx ts-node test/restore-database.ts <备份目录>');
    console.log('示例: npx ts-node test/restore-database.ts ./backup/backup-2025-01-23');
    process.exit(1);
  }

  const restore = new DatabaseRestore(backupDir);
  
  try {
    await restore.restoreDatabase();
    await restore.verifyRestore();
    
    console.log('\n🎉 数据库恢复完成！');
    
  } catch (error) {
    console.error('恢复失败:', error);
    process.exit(1);
  } finally {
    await prisma.$disconnect();
  }
}

// 如果直接运行此脚本
if (require.main === module) {
  main();
}

export default DatabaseRestore;
